package com.unitd.base.frame.plugin.codegen;

import com.google.common.collect.Maps;
import com.unitd.base.frame.util.DateUtils;
import com.unitd.base.frame.util.StringUtils;
import freemarker.template.Template;

import java.io.*;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.util.*;

/**
 * 代码生成器
 *
 * @author Hudan
 * @version 1.0
 * @filename CodeGenerateUtils.java
 * @copyright www.unitd.com
 * @date 2020-06-20
 */
public class CodeGenerateUtils {

    /** Java代码存储根目录 */
    private String javaFilePath;
    /** 资源文件存储根目录 */
    private String resourceFilePath;
    /** 单元测试类存储根目录 */
    private String testFilePath;
    /** Java代码的根包名 */
    private String packageName;
    /** Java代码的模块名(子包名) */
    private String moduleName;

    /** 需要生成代码的数据库表名 */
    private String tableName;
    /** 需要生成代码的数据库表描述 */
    private String tableDesc;
    /** 需要生成代码的数据库表对应的Java实体类名 */
    private String className;
    /** 生成代码的作者名 */
    private String author;

    /** 生成代码时需要调用的数据库JDBC驱动 */
    private String driver;
    /** 生成代码时需要调用的数据库JDBCURL */
    private String url;
    /** 生成代码时需要调用的数据库用户名 */
    private String user;
    /** 生成代码时需要调用的数据库密码 */
    private String password;
    /** 生成代码的日期 */
    private String currDate = DateUtils.format2DateStr(new Date(), "yyyy-MM-dd");

    /**
     * 构造方法
     */
    public CodeGenerateUtils(String javaFilePath, String resourceFilePath, String testFilePath,
                             String packageName, String moduleName, String tableName, String tableDesc, String className, String author,
                             String driver, String url, String user, String password) {
        this.javaFilePath = javaFilePath;
        this.resourceFilePath = resourceFilePath;
        this.testFilePath = testFilePath;
        this.packageName = packageName;
        this.moduleName = moduleName;
        this.tableName = tableName;
        this.tableDesc = tableDesc;
        this.className = className;
        this.author = author;
        this.driver = driver;
        this.url = url;
        this.user = user;
        this.password = password;
    }

    /**
     * 获取数据库连接
     *
     * @return 数据库连接对象
     */
    public Connection getConnection() throws Exception {
        Class.forName(driver);
        return DriverManager.getConnection(url, user, password);
    }

    public void generate() {
        try {
            Connection connection = getConnection();
            DatabaseMetaData databaseMetaData = connection.getMetaData();
            ResultSet resultSet = databaseMetaData.getColumns(null, "%", tableName, "%");

            List<ColumnClass> columnClassList = new ArrayList<>();
            ColumnClass columnClass;
            String columnName;
            while (resultSet.next()) {
                // 列名
                columnName = resultSet.getString("COLUMN_NAME");

                //id、create_time、update_time 字段略过
                if (resultSet.getString("COLUMN_NAME").toLowerCase().equals("id")) {
                    continue;
                }
                if (resultSet.getString("COLUMN_NAME").toLowerCase().equals("create_time")) {
                    continue;
                }
                if (resultSet.getString("COLUMN_NAME").toLowerCase().equals("update_time")) {
                    continue;
                }

                columnClass = new ColumnClass();
                //获取字段名称
                columnClass.setColumnName(columnName);
                //获取字段类型
                columnClass.setColumnType(resultSet.getString("TYPE_NAME"));
                //转换字段名称，如 sys_name 变成 SysName
                columnClass.setChangeColumnName(StringUtils.toCamelCase(columnName));
                //字段在数据库的注释
                columnClass.setColumnComment(resultSet.getString("REMARKS"));
                columnClassList.add(columnClass);
            }
            Map<String, Object> dataMap = Maps.newHashMap();
            dataMap.put("model_column", columnClassList);

//            /* 生成实体Entity类 */
//            generateModelFile(resultSet, dataMap);
//            //生成Mapper文件
//            generateMapperFile(resultSet, dataMap);
//            // 生成Dao文件
//            generateDaoFile(resultSet);
            //生成服务层接口文件
            generateServiceFile();
            //生成服务实现层文件
            generateServiceImplFile();
//
//            /* 生成实体DTO类 */
//            generateDTOFile(resultSet, dataMap);
//            /* 生成provider文件 */
//            generateProviderFile();
//            /* 生成providerImpl文件 */
//            generateProviderImplFile();

            //生成Controller层文件
            generateControllerFile();

            // 生成单元测试类
            generateTestFile(resultSet, dataMap);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 生成实体类类
     *
     * @param resultSet 查询的数据结果集
     * @param dataMap   需要读取到模板文件上的数据模型
     * @throws Exception
     */
    private void generateModelFile(ResultSet resultSet, Map<String, Object> dataMap) throws Exception {
        final String suffix = ".java";
        final String path = javaFilePath + packageName.replace(".", "/") + "/module/" + moduleName + "/model/" + className + suffix;
        final String templateName = "Model.ftl";

        File mapperFile = new File(path);
        if (!mapperFile.getParentFile().exists()) mapperFile.getParentFile().mkdirs();

        generateFileByTemplate(templateName, mapperFile, dataMap);
    }

    /**
     * 生成DTO类
     *
     * @param resultSet 查询的数据结果集
     * @param dataMap   需要读取到模板文件上的数据模型
     * @throws Exception
     */
    private void generateDTOFile(ResultSet resultSet, Map<String, Object> dataMap) throws Exception {
        final String suffix = "DTO.java";
        final String path = javaFilePath + packageName.replace(".", "/") + "/module/" + moduleName + "/dto/" + className + suffix;
        final String templateName = "DTO.ftl";

        File mapperFile = new File(path);
        if (!mapperFile.getParentFile().exists()) mapperFile.getParentFile().mkdirs();

        generateFileByTemplate(templateName, mapperFile, dataMap);
    }

    /**
     * 生成Mapper类
     *
     * @throws Exception
     */
    private void generateDaoFile() throws Exception {
        final String suffix = "Mapper.java";
        final String path = javaFilePath + packageName.replace(".", "/") + "/module/" + moduleName + "/mybatis/mapper/" + className + suffix;
        final String templateName = "Dao.ftl";

        generateFileByTemplate(templateName, path);
    }

    /**
     * 生成Mappe文件
     *
     * @param dataMap 需要读取到模板文件上的数据模型
     * @throws Exception
     */
    private void generateMapperFile(Map<String, Object> dataMap) throws Exception {
        final String suffix = "Mapper.xml";
        final String path = resourceFilePath + "/mybatis/mapper/" + moduleName + className + suffix;
        final String templateName = "Mapper.ftl";

        File mapperFile = new File(path);
        if (!mapperFile.getParentFile().exists()) mapperFile.getParentFile().mkdirs();

        generateFileByTemplate(templateName, mapperFile, dataMap);
    }

    /**
     * 生成Service接口类 com.unitd.base.module.logistics.service;
     *
     * @throws Exception
     */
    private void generateServiceFile() throws Exception {
        final String suffix = "Service.java";
        final String path = javaFilePath + packageName.replace(".", "/") + "/module/" + moduleName + "/service/" + className + suffix;
        final String templateName = "Service.ftl";
        generateFileByTemplate(templateName, path);
    }

    /**
     * 生成ServiceImpl接口实现类
     *
     * @throws Exception
     */
    private void generateServiceImplFile() throws Exception {
        final String suffix = "ServiceImpl.java";
        final String path = javaFilePath + packageName.replace(".", "/") + "/module/" + moduleName + "/service/impl/" + className + suffix;
        final String templateName = "ServiceImpl.ftl";

        generateFileByTemplate(templateName, path);
    }

    /**
     * 生成Provider接口类
     *
     * @throws Exception
     */
    private void generateProviderFile() throws Exception {
        final String suffix = "Provider.java";
        final String path = javaFilePath + packageName.replace(".", "/") + "/module/" + moduleName + "/provider/" + className + suffix;
        final String templateName = "Proider.ftl";
        File mapperFile = new File(path);

        generateFileByTemplate(templateName, path);
    }

    /**
     * 生成ProviderImpl接口实现类
     *
     * @throws Exception
     */
    private void generateProviderImplFile() throws Exception {
        final String suffix = "ProviderImpl.java";
        final String path = javaFilePath + packageName.replace(".", "/") + "/module/" + moduleName + "/provider/impl/" + className + suffix;
        final String templateName = "ProviderImpl.ftl";

        generateFileByTemplate(templateName, path);
    }

    /**
     * 生成Controller类
     *
     * @throws Exception
     */
    private void generateControllerFile() throws Exception {
        final String suffix = "Controller.java";
        final String path = javaFilePath + packageName.replace(".", "/") + "/module/" + moduleName + "/controller/" + className + suffix;
        final String templateName = "Controller.ftl";

        generateFileByTemplate(templateName, path);
    }

    /**
     * 生成单元测试类
     *
     * @param resultSet
     */
    private void generateTestFile(ResultSet resultSet, Map<String, Object> dataMap) throws Exception {
        final String prefix = "Test";
        final String path = testFilePath + packageName.replace(".", "/") + "/module/" + moduleName + "/mybatis/mapper/" + prefix + className + "Mapper.java";
        final String templateName = "Test.ftl";

        File mapperFile = new File(path);
        if (!mapperFile.getParentFile().exists()) {
            mapperFile.getParentFile().mkdirs();
        }

        generateFileByTemplate(templateName, mapperFile, dataMap);
    }

    /**
     * 根据模板文件生成相应的代码文件
     *
     * @param templateName 模板文件名称
     * @param path         需要生成的目标文件全路径
     */
    private void generateFileByTemplate(String templateName, String path) throws Exception {
        File mapperFile = new File(path);

        if (!mapperFile.getParentFile().exists()) mapperFile.getParentFile().mkdirs();

        Map<String, Object> dataMap = new HashMap<>();
        generateFileByTemplate(templateName, mapperFile, dataMap);
    }

    /**
     * 真正根据freemark模板生成代码的地方
     *
     * @param templateName 模板文件名称
     * @param file         需要生成的目标文件全路径
     * @param dataMap      参数集合
     * @throws Exception
     */
    private void generateFileByTemplate(final String templateName, File file, Map<String, Object> dataMap) throws Exception {
        Template template = FreeMarkerTemplateUtils.getTemplate(templateName);
        FileOutputStream fos = new FileOutputStream(file);
        dataMap.put("tableName", tableName);
        dataMap.put("tableDesc", tableDesc);
        dataMap.put("packageName", packageName);
        dataMap.put("moduleName", moduleName);
        dataMap.put("className", className);
        dataMap.put("author", author);
        dataMap.put("date", currDate);
        Writer out = new BufferedWriter(new OutputStreamWriter(fos, "utf-8"), 10240);
        template.process(dataMap, out);
    }
}